import { Vue } from './install'
import {forEach} from './util'
import ModuleCollection from './module/module-collection'
import store from '../store';
function getNewState(store, path) {
    return path.reduce((memo, current) => {
        return memo[current];
    }, store.state)
}

function installModule(store,rootState,path,module) {
    let namespace = store._modules.getNamespace(path);
    if(path.length>0){
       let parent = path.slice(0,-1).reduce((memo,current)=>{
            return memo[current]
        },rootState)
        // ['a','c','d']
        store._withCommitting(()=>{
            Vue.set(parent,path[path.length-1], module.state)
        })
    }
    module.forEachGetter((fn,key)=>{
        store._wrappedGetters [namespace+key]=()=>{
           return fn.call(store,getNewState(store, path))
        }
    })
    module.forEachMutation((fn,key)=>{
        store.mutations[namespace+key]=store.mutations[namespace+key] || [];
        store.mutations[namespace+key].push((payload)=>{
            store._withCommitting(()=>{
                fn.call(store,getNewState(store, path),payload)
            })
            
            store._subscribe.forEach(fn=>fn({ type: namespace + key, payload }, store.state)) 
        })
    })
    module.forEachAction((fn,key)=>{
        store.actions[namespace+key]=store.actions[namespace+key] || [];
        store.actions[namespace+key].push((payload)=>{
            return fn.call(store,store,payload)
        })
    })
    module.forEachChild((child,key)=>{
        installModule(store,rootState,path.concat(key),child)
    })
}
function restoreVm(store,state) {
    let oldVm = store._vm
    store.getters ={}; 
    const computed = {}
    forEach(store._wrappedGetters,(fn,key)=>{
        computed[key]=()=>{
            return fn()
           }
           Object.defineProperty(store.getters,key,{
               get:()=>store._vm[key]
           })
       })
       
    
       store._vm = new Vue({
        data:{
            $$state:state
        },
        computed
    })
    if(store.strict){
        store._vm.$watch(()=>store._vm._data.$$state,()=>{
            console.assert(store._committing,'非法更改')
        },{deep:true,sync:true})
    }
    if(oldVm){
        Vue.nextTick(()=>oldVm.$destroy())
    }
}
class Store {
    constructor(options){
        // 对用户模块进行整合 
     this._modules = new ModuleCollection(options) // 对用户的参数进行格式化操作
     this.mutations ={};
     this.actions ={};
     this._wrappedGetters ={}
     this._subscribe =[]
     this._committing = false ; //默认不是在mutation中更改的 
     this.strict =options.strict; 
     // 将用户的module 重新安装
     const state = options.state
     installModule(this,state,[],this._modules.root)
     
     restoreVm(this,state)

     if(options.plugins){
        options.plugins.forEach(plugins=>plugins.call(this,this))
     }
     
    }
    subscribe(fn){
        this._subscribe.push(fn)
    }
    replaceState(newState){
        this._withCommitting(()=>{
            this._vm._data.$$state = newState
        })
    }
     
    get state(){
        return this._vm._data.$$state
    }
    registerModule(path,module){
        if(typeof path ==='string')path =[path];
        this._modules.register(path,module)//注册成对应的对象
        installModule(this,this.state,path,module.newModule) //安装
        restoreVm(this,this.state)
    }
    _withCommitting(fn){
        this._committing = true 
        fn()
        this._committing = false
    }
    commit =(mutationName,payload)=>{
        this.mutations[mutationName]&&this.mutations[mutationName]
        .forEach(fn=> fn.call(this, payload))
    }
    dispatch=(actionsName,payload)=>{
        console.log(this.actions)
        this.actions[actionsName]&&this.actions[actionsName]
        .forEach(fn=> fn.call(this, payload))
    }

    

}

export default Store ; 